import { Vector3, VectorXZ } from "@minecraft/server";
import { Dimensions } from "@sapi-game/constants";
import { CubeRegion } from "@sapi-game/gameRegion/gameRegion";
import { Game, GameComponent } from "@sapi-game/main";
import { Duration, Vector3Utils } from "@sapi-game/utils";
import { EffectIds } from "@sapi-game/utils/vanila-data";
import { PillarOfFortuneMainState } from "../states/mainState";

export class PillarOfFortuneBorder extends GameComponent<PillarOfFortuneMainState> {
    private curRegion?: CubeRegion;
    private chunkPos: Vector3[][] = [];

    override onAttach(): void {
        this.curRegion = this.context.map.region;
        this.subscribe(
            Game.events.interval,
            () => this.showBorder(),
            new Duration(40)
        );
        this.subscribe(
            Game.events.interval,
            () => this.addEffect(),
            new Duration(10)
        );
        this.updateChunkPos();
    }

    startShrink() {
        this.subscribe(
            Game.events.interval,
            () => {
                if (this.curRegion) {
                    this.curRegion = this.curRegion.inSet({ x: 1, y: 0, z: 1 });
                    this.updateChunkPos(); //缩小后更新点
                }
            },
            Duration.fromSeconds(15)
        );
    }

    /**更新边界粒子点 */
    updateChunkPos() {
        if (!this.curRegion) return;
        const max = Vector3Utils.add(this.curRegion.getMax(), {
            x: 1,
            y: 1,
            z: 1,
        });
        this.chunkPos = linspaceCubeSurface(
            this.curRegion.getMin(),
            max,
            10,
            12,
            10,
            { top: false, bottom: false }
        );
    }

    /**显示边界 */
    private showBorder() {
        if (!this.curRegion) return;
        try {
            for (const chunk of this.chunkPos) {
                try {
                    chunk.forEach((loc) => {
                        Dimensions.Overworld.spawnParticle(
                            "minecraft:endrod",
                            loc
                        );
                    });
                } catch (err) {}
            }
        } catch (err) {}
    }

    /**给超出边界的玩家凋零效果 */
    addEffect() {
        this.context.players.forEach((p) => {
            if (!p.alive) return;
            if (!this.curRegion) {
                p.addEffect(EffectIds.Wither, 20, 2, true);
                p.actionbar("§v你在圈外，受到凋零效果");
            } else if (!this.curRegion.isInside(p.player.location)) {
                p.addEffect(EffectIds.Wither, 20, 2, true);
                p.actionbar("§v你在圈外，受到凋零效果");
            }
        });
    }
}

/**
 * 在立方体外表面上高效地生成均匀插值点，可选择性生成面。
 *
 * @param start 立方体的起始点 (min corner)
 * @param end 立方体的结束点 (max corner)
 * @param nx X轴方向的点数
 * @param ny Y轴方向的点数
 * @param nz Z轴方向的点数
 * @param options 一个对象，用于指定包含哪些面，默认为全部包含
 */
function linspaceCubeSurface(
    start: Vector3,
    end: Vector3,
    nx: number,
    ny: number,
    nz: number,
    options?: {
        top?: boolean; // +Y 面
        bottom?: boolean; // -Y 面
        left?: boolean; // -X 面
        right?: boolean; // +X 面
        front?: boolean; // -Z 面
        back?: boolean; // +Z 面
    }
): Vector3[][] {
    // 默认生成所有6个面
    const config = {
        top: true,
        bottom: true,
        left: true,
        right: true,
        front: true,
        back: true,
        ...options,
    };

    const pointMap: Map<string, Vector3[]> = new Map();
    const getOrCreateChunk = (cx: number, cz: number) => {
        const key = `${cx}=${cz}`;
        let arr = pointMap.get(key);
        if (!arr) {
            arr = [];
            pointMap.set(key, arr);
        }
        return arr;
    };

    const getStep = (startVal: number, endVal: number, n: number) =>
        n > 1 ? (endVal - startVal) / (n - 1) : 0;

    const stepX = getStep(start.x, end.x, nx);
    const stepY = getStep(start.y, end.y, ny);
    const stepZ = getStep(start.z, end.z, nz);

    // 1. 前后两个面 (-Z 和 +Z)
    for (let iz of [0, nz - 1]) {
        if ((iz === 0 && !config.front) || (iz === nz - 1 && !config.back))
            continue;

        const z = start.z + iz * stepZ;
        const cz = z >> 4;
        let prevCX = undefined;
        let arr: Vector3[] | undefined;
        for (let ix = 0; ix < nx; ix++) {
            const x = start.x + ix * stepX;
            const cx = x >> 4;
            if (cx !== prevCX) {
                arr = getOrCreateChunk(cx, cz);
                prevCX = cx;
            }
            //遍历y轴
            for (let iy = 0; iy < ny; iy++) {
                const y = start.y + iy * stepY;
                arr!.push({ x, y, z });
            }
        }
    }

    // 2. 左右两个面 (-X 和 +X)
    for (const ix of [0, nx - 1]) {
        if ((ix === 0 && !config.left) || (ix === nx - 1 && !config.right))
            continue;
        const x = start.x + ix * stepX;
        const cx = x >> 4;

        let prevCZ: number | undefined;
        let arr: Vector3[] | undefined;

        for (let iz = 0; iz < nz; iz++) {
            const z = start.z + iz * stepZ;
            const cz = z >> 4;
            if (cz !== prevCZ) {
                arr = getOrCreateChunk(cx, cz);
                prevCZ = cz;
            }
            for (let iy = 0; iy < ny; iy++) {
                const y = start.y + iy * stepY;
                arr!.push({ x, y, z });
            }
        }
    }

    // 3. 上下两个面 (-Y 和 +Y)
    for (const iy of [0, ny - 1]) {
        if ((iy === 0 && !config.bottom) || (iy === ny - 1 && !config.top))
            continue;
        const y = start.y + iy * stepY;

        let prevCX: number | undefined;
        let prevCZ: number | undefined;
        let arr: Vector3[] | undefined;

        for (let iz = 0; iz < nz; iz++) {
            const z = start.z + iz * stepZ;
            const cz = z >> 4;

            for (let ix = 0; ix < nx; ix++) {
                const x = start.x + ix * stepX;
                const cx = x >> 4;

                if (cx !== prevCX || cz !== prevCZ) {
                    arr = getOrCreateChunk(cx, cz);
                    prevCX = cx;
                    prevCZ = cz;
                }
                arr!.push({ x, y, z });
            }
        }
    }

    return [...pointMap.values()];
}

function BuildChunkKey(chunk: VectorXZ) {
    return `${chunk.x}=${chunk.z}`;
}
